`timescale 1ns/100ps

`include "sim_glb.sv"

module tc;

localparam          CLK_PRD                 = 5;
localparam          DEPTH                   = 12;
localparam          OUT_REG_EN              = 1'b1;
localparam          DATA_BW                 = 8;
localparam          ADDR_BW                 = $clog2(DEPTH);    // address range [0:DEPTH-1]
localparam          CNT_BW                  = $clog2(DEPTH + 1);// counter range [0:DEPTH]

reg                                         rst_wr_n;
reg                                         clk_wr;
reg                                         rst_rd_n;
reg                                         clk_rd;

reg                                         wen;
reg                                         ren;

wire                                        fifo_wen;
wire                                        fifo_full;
wire                                        fifo_afull;
wire                [CNT_BW-1:0]            fifo_wcnt;

wire                                        fifo_ren;
wire                                        fifo_empty;
wire                                        fifo_aempty;
wire                [CNT_BW-1:0]            fifo_rcnt;

reg                 [ADDR_BW-1:0]           cfg_fifo_afull_th;  // afull_th<fifo_cnt<=DEPTH is afull
reg                 [ADDR_BW-1:0]           cfg_fifo_aempty_th; // 0<=fifo_cnt<aempty_th   is aempty
wire                                        err_fifo_wilf;      // fifo internal logic fault
wire                                        err_fifo_wof;       // fifo write overflow
wire                                        err_fifo_rilf;
wire                                        err_fifo_ruf;       // fifo read underflow

wire                [DATA_BW-1:0]           fifo_wdata;
wire                [DATA_BW-1:0]           fifo_rdata;

reg                 [DATA_BW-1:0]           gen_wdata;
reg                 [DATA_BW-1:0]           chk_rdata;
reg                                         err_flag;

initial begin:CRG
    rst_wr_n=1'b0;
    clk_wr=1'b0;

    rst_rd_n=1'b0;
    clk_rd=1'b0;

    fork
        rst_wr_n=#100.5 1'b1;
        rst_rd_n=#100.5 1'b1;
        forever clk_wr=#4 ~clk_wr;
        forever clk_rd=#5 ~clk_rd;
    join
end

RGRS_MNG    rgrs;
initial begin:REGRESS
    rgrs = new("tc_tiny_afifo", 2);

    rgrs.wait_chks_done(100_000_000);
end

initial begin:GEN_WEN
    integer             tx_num;
    integer             rnd;

    wen = 1'b0;

    cfg_fifo_afull_th  = DEPTH-1;  // afull_th<fifo_cnt<=DEPTH is afull
    cfg_fifo_aempty_th = 1; // 0<=fifo_cnt<=aempty_th   is aempty

    @(posedge rst_wr_n);

//    repeat(32) begin
//        @(posedge clk_wr);
//        wen = `U_DLY 1'b1;
//    end
//    wen = `U_DLY 1'b0;
//    #500;

    tx_num=0;
    @(posedge clk_wr);
    while(tx_num<100_000) begin
        rnd = $urandom_range(1,100);

        if (rnd<=60) begin
            wen = `U_DLY 1'b1;
            @(posedge clk_wr);
            while(fifo_full==1'b1) begin
                @(posedge clk_wr);
            end

            tx_num = tx_num + 1;
        end
        else begin
            wen = `U_DLY 1'b0;
            @(posedge clk_wr);
        end
    end
    wen = `U_DLY 1'b0;

    rgrs.one_chk_done("wen num is done.");
end
assign fifo_wen = wen & (~fifo_full);

always@(posedge clk_wr or negedge rst_wr_n) begin
    if (rst_wr_n==1'b0) begin
        gen_wdata <=`U_DLY 0;
    end else begin
        if (fifo_wen==1'b1) begin
            gen_wdata <=`U_DLY gen_wdata + 1'd1;
        end
    end
end
assign fifo_wdata = gen_wdata;

tiny_afifo #(
        .DEPTH                          (DEPTH                          ),      // must DEPTH=2*n+1 (n>=1) if OUT_REG_EN==1'b1, else DEPTH=2*n (n>=1)
        .DATA_BW                        (DATA_BW                        ),
        .OUT_REG_EN                     (OUT_REG_EN                     ),	// read data is combinatioanl logic
        .SYNC_NUM_W2R                   (3                              ),
        .SYNC_NUM_R2W                   (3                              )
) u_tiny_afifo ( 
        .rst_wr_n                       (rst_wr_n                       ),
        .clk_wr                         (clk_wr                         ),

        .fifo_wen                       (fifo_wen                       ),
        .fifo_wdata                     (fifo_wdata                     ),
        .fifo_full                      (fifo_full                      ),
        .fifo_afull                     (fifo_afull                     ),
        .fifo_wcnt                      (fifo_wcnt                      ),

        .cfg_fifo_afull_th              (cfg_fifo_afull_th              ),	// afull_th<fifo_cnt<
        .err_fifo_wilf                  (err_fifo_wilf                  ),	// fifo internal logic fault
        .err_fifo_wof                   (err_fifo_wof                   ),	// fifo write overflow

        .rst_rd_n                       (rst_rd_n                       ),
        .clk_rd                         (clk_rd                         ),

        .fifo_ren                       (fifo_ren                       ),
        .fifo_rdata                     (fifo_rdata                     ),
        .fifo_empty                     (fifo_empty                     ),
        .fifo_aempty                    (fifo_aempty                    ),
        .fifo_rcnt                      (fifo_rcnt                      ),
        .prefetch_cnt                   (                               ),	// number of data cached in out REG, not used when OUT_REG_EN

        .cfg_fifo_aempty_th             (cfg_fifo_aempty_th             ),	// 0<
        .err_fifo_rilf                  (err_fifo_rilf                  ),	// fifo internal logic fault
        .err_fifo_ruf                   (err_fifo_ruf                   )	// fifo read underflow
);

initial begin:GEN_REN
    integer             rx_num;
    integer             rnd;

    ren = 1'b0;

    @(posedge rst_rd_n);

//    repeat(32) begin
//        @(posedge clk_rd);
//        ren = `U_DLY 1'b1;
//    end
//    ren = `U_DLY 1'b0;
//    #500;

    rx_num=0;
    @(posedge clk_rd);
    while(rx_num<100_000) begin
        rnd = $urandom_range(1,100);

        if (rnd<=80) begin
            ren = `U_DLY 1'b1;
            @(posedge clk_rd);
            while(fifo_empty==1'b1) begin
                @(posedge clk_rd);
            end

            rx_num = rx_num + 1;
        end
        else begin
            ren = `U_DLY 1'b0;
            @(posedge clk_rd);
        end
    end
    ren = `U_DLY 1'b0;

    rgrs.one_chk_done("ren num is done.");
end
assign fifo_ren = ren & (~fifo_empty);

always@(posedge clk_rd or negedge rst_rd_n) begin
    if (rst_rd_n==1'b0) begin
        chk_rdata <=`U_DLY 0;
        err_flag  <=`U_DLY 1'b0;
    end else begin
        if (fifo_ren==1'b1) begin
            chk_rdata <=`U_DLY fifo_rdata + 1'd1;

            if (chk_rdata!=fifo_rdata) begin
                $error("chk_rdata %h is not same to fifo_rdata %h", chk_rdata, fifo_rdata);
                err_flag <=`U_DLY 1'b1;
                $stop;
            end
        end
    end
end

endmodule

